package cn.felord.security.autoconfigure.resourceserver;

import cn.felord.security.autoconfigure.PermissionService;
import cn.felord.security.autoconfigure.ResourcePermissionEvaluator;
import cn.felord.security.autoconfigure.TokenGenerator;
import cn.felord.security.autoconfigure.handler.LoginAuthenticationSuccessHandler;
import cn.felord.security.autoconfigure.handler.SimpleAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
import org.springframework.security.web.SecurityFilterChain;

import java.util.Collections;
import java.util.List;

/**
 * 资源服务器配置
 *
 * @author felord.cn
 * @since 2021 /8/7 18:05
 */
@Configuration(proxyBeanMethods = false)
public class ResourceServerConfiguration {
    private List<ResourceServerSecurityCustomizer> resourceServerSecurityCustomizers = Collections.emptyList();

    /**
     * 资源过滤器链
     *
     * @param httpSecurity the http security
     * @return the security filter chain
     * @throws Exception the exception
     */
    @Bean
    SecurityFilterChain resourceServerSecurityFilterChain(HttpSecurity httpSecurity, TokenGenerator<?> tokenGenerator, UserDetailsService userDetailsService) throws Exception {
        httpSecurity.cors(Customizer.withDefaults())
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(sessionManagementCustomizer ->
                        sessionManagementCustomizer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling(configurer ->
                        configurer.authenticationEntryPoint(new SimpleAuthenticationEntryPoint()))
                .oauth2ResourceServer(resourceServerConfig ->
                        resourceServerConfig.authenticationEntryPoint(new SimpleAuthenticationEntryPoint())
                                .jwt(Customizer.withDefaults())
                )
                .addFilterAfter(new RefreshTokenFilter(new LoginAuthenticationSuccessHandler(tokenGenerator, userDetailsService)), BearerTokenAuthenticationFilter.class);
        this.resourceServerSecurityCustomizers.forEach(resourceServerSecurityCustomizer -> resourceServerSecurityCustomizer.customize(httpSecurity));
        return httpSecurity.build();
    }

    /**
     * 开启自定义注解策略
     * 接口不标注注解为默认放行，标注则需要角色权限管控。
     *
     * @return the resource permission evaluator
     */
    @Bean
    ResourcePermissionEvaluator permissionEvaluator(PermissionService permissionService) {
        return new ResourcePermissionEvaluator(permissionService);
    }

    /**
     * Jwt认证转换器
     *
     * @return the jwt authentication converter
     */
    @Bean
    JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
//        OAuth2 默认前缀是 SCOPE_     Spring Security 是 ROLE_
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix("");
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }

    /**
     * Sets web security customizers.
     *
     * @param resourceServerSecurityCustomizers the http security customizers
     */
    @Autowired(required = false)
    void setWebSecurityCustomizers(List<ResourceServerSecurityCustomizer> resourceServerSecurityCustomizers) {
        this.resourceServerSecurityCustomizers = resourceServerSecurityCustomizers;
    }
}
